package xin.marcher.module.diplomacy.repository;

import com.alibaba.fastjson.JSON;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.text.Text;
import org.elasticsearch.common.xcontent.*;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.SearchModule;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.elasticsearch.search.sort.ScoreSortBuilder;
import org.elasticsearch.search.sort.SortOrder;
import org.elasticsearch.search.suggest.SuggestBuilder;
import org.elasticsearch.search.suggest.SuggestBuilders;
import org.elasticsearch.search.suggest.completion.CompletionSuggestion;
import org.elasticsearch.search.suggest.completion.CompletionSuggestionBuilder;
import org.springframework.stereotype.Repository;
import xin.marcher.module.diplomacy.domain.dto.CompletionSearchWordsDTO;
import xin.marcher.module.diplomacy.domain.dto.PorductSearchDTO;
import xin.marcher.module.diplomacy.domain.query.CompletionSearchWordQuery;
import xin.marcher.module.diplomacy.domain.query.ProductSearchQuery;
import xin.marcher.module.diplomacy.domain.query.ProductStructuredQuery;

import javax.annotation.Resource;
import java.io.IOException;
import java.util.*;

/**
 * 商品搜索
 */
@Repository
public class ProductSearchRepository {

    private static final String MY_SUGGEST = "my_suggest";

    @Resource
    private RestHighLevelClient restHighLevelClient;

    /**
     * 搜索词补全
     */
    public CompletionSearchWordsDTO listCompletionSearchWords(CompletionSearchWordQuery request) throws IOException {
        // 1、构建 CompletionSuggestion 条件
        CompletionSuggestionBuilder completionSuggestionBuilder = SuggestBuilders.completionSuggestion(request.getFieldName());
        completionSuggestionBuilder.prefix(request.getText());
        completionSuggestionBuilder.skipDuplicates(true);
        completionSuggestionBuilder.size(request.getCount());

        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        searchSourceBuilder.sort(new ScoreSortBuilder().order(SortOrder.DESC));
        searchSourceBuilder.suggest(new SuggestBuilder().addSuggestion(MY_SUGGEST, completionSuggestionBuilder));

        // 2、封装搜索请求
        SearchRequest searchRequest = new SearchRequest();
        searchRequest.indices(request.getIndexName());
        searchRequest.source(searchSourceBuilder);

        // 3、查询elasticsearch
        SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);

        // 4、获取响应中的补全的词的列表
        CompletionSuggestion completionSuggestion = searchResponse.getSuggest().getSuggestion(MY_SUGGEST);
        List<CompletionSuggestion.Entry.Option> options = completionSuggestion.getEntries().get(0).getOptions();

        List<String> result = new ArrayList<>();
        for (CompletionSuggestion.Entry.Option option : options) {
            result.add(option.getText().string());
        }

        return new CompletionSearchWordsDTO(result);
    }

    /**
     * 商品搜索接口
     */
    public PorductSearchDTO searchProducts(ProductSearchQuery request) throws IOException {
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        searchSourceBuilder.trackTotalHits(true);

        // 1、构建match条件
        request.getQueryTexts().forEach((field, text) -> {
            searchSourceBuilder.query(QueryBuilders.matchQuery(field, text));
        });

        // 2、设置搜索高亮配置
        HighlightBuilder highlightBuilder = new HighlightBuilder();
        highlightBuilder.field(request.getHighLightField());
        highlightBuilder.preTags("<span stype=color:red>");
        highlightBuilder.postTags("</span>");
        highlightBuilder.numOfFragments(0);
        searchSourceBuilder.highlighter(highlightBuilder);

        // 3、设置搜索分页参数
        int from = (request.getPageNum() - 1) * request.getPageSize();
        searchSourceBuilder.from(from);
        searchSourceBuilder.size(request.getPageSize());

        // 4、封装搜索请求
        SearchRequest searchRequest = new SearchRequest(request.getIndexName());
        searchRequest.source(searchSourceBuilder);

        // 5、查询elasticsearch
        SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);

        // 6、对结果进行高亮处理
        SearchHits hits = searchResponse.getHits();
        for (SearchHit hit : hits) {
            HighlightField highlightField = hit.getHighlightFields().get(request.getHighLightField());
            Map<String, Object> sourceAsMap = hit.getSourceAsMap();
            Text[] fragments = highlightField.fragments();
            StringBuilder builder = new StringBuilder();
            for (Text fragment : fragments) {
                builder.append(fragment.string());
            }
            sourceAsMap.put(request.getHighLightField(), builder.toString());
        }

        // 7. 封装返回结果
        return buildPorductSearchDTO(hits, request.getPageNum(), request.getPageSize());
    }


    /**
     * 商品结构化查询
     */
    public PorductSearchDTO structuredSearchProducts(ProductStructuredQuery request) throws IOException {
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        searchSourceBuilder.trackTotalHits(true);

        // 1、解析queryDSL
        String queryDsl = JSON.toJSONString(request.getQueryDsl());
        SearchModule searchModule = new SearchModule(Settings.EMPTY, false, Collections.emptyList());
        NamedXContentRegistry namedXContentRegistry = new NamedXContentRegistry(searchModule.getNamedXContents());
        XContent xContent = XContentFactory.xContent(XContentType.JSON);
        XContentParser xContentParser = xContent.createParser(namedXContentRegistry, LoggingDeprecationHandler.INSTANCE, queryDsl);
        searchSourceBuilder.parseXContent(xContentParser);

        // 2、设置搜索分页参数
        int from = (request.getPageNum() - 1) * request.getPageSize();
        searchSourceBuilder.from(from);
        searchSourceBuilder.size(request.getPageSize());

        // 3、封装搜索请求
        SearchRequest searchRequest = new SearchRequest(request.getIndexName());
        searchRequest.source(searchSourceBuilder);

        // 4、查询elasticsearch
        SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);

        // 5. 封装返回结果
        return buildPorductSearchDTO(searchResponse.getHits(), request.getPageNum(), request.getPageSize());
    }

    private PorductSearchDTO buildPorductSearchDTO(SearchHits hits, int pageNum, int pageSize) {
        PorductSearchDTO resultDTO = new PorductSearchDTO();
        List<String> skuIds = new ArrayList<>();
        Set<PorductSearchDTO.BrandInfo> brandInfos = new HashSet<>();
        for (SearchHit hit : hits.getHits()) {
            skuIds.add(hit.getId());
            Object brandIdStr = hit.getSourceAsMap().get("brandId");
            long brandId = -1L;
            String brandName = "";
            if (Objects.nonNull(brandIdStr)) {
                brandId = Long.parseLong(String.valueOf(brandIdStr));
                brandName = (String) hit.getSourceAsMap().get("brandName");
            }
            brandInfos.add(new PorductSearchDTO.BrandInfo(brandId, brandName));
        }
        resultDTO.setSkuIds(skuIds);
        resultDTO.setBrandInfos(brandInfos);
        resultDTO.setTotalCount(hits.getTotalHits().value);
        resultDTO.setPageNum(pageNum);
        resultDTO.setPageSize(pageSize);
        return resultDTO;
    }

}
